/**
 * $id:kiwi_001

 * Copyright(c) 2006 Adobe Systems Incorporated. All Rights Reserved.
 */
package ism.api
{
	import com.adobe.kiwi.atompub.model.IWorkspace;
	//import com.adobe.kiwi.atompub.model.impl.Collection;
	import com.adobe.kiwi.atompub.*;
	import com.adobe.kiwi.atompub.events.*;
	import com.adobe.kiwi.atompub.model.*;
	import com.adobe.kiwi.atompub.model.impl.*;
	import com.adobe.kiwi.atompub.utils.*;
	import com.adobe.kiwi.connections.*;
	import com.adobe.kiwi.feeds.*;
	import com.adobe.kiwi.feeds.atom.*;	
	import com.adobe.net.*;
	
	import flash.events.*;
	
	import mx.rpc.*;
	import mx.rpc.events.*;
	import mx.rpc.http.*


		
	/**
	 *  Dispatched when the Service (introspection) document has been retrieved.
	 * 
	 *  @eventType com.adobe.kiwi.atompub.events.GetServiceEvent
	 **/
	[Event(name="getService", type="com.adobe.kiwi.atompub.events.GetServiceEvent")]
	
	/**
	 *  Dispatched when a Feed has been retrieved.
	 * 
	 *  @eventType com.adobe.kiwi.atompub.events.GetFeedEvent
	 **/
	[Event(name="getFeed", type="com.adobe.kiwi.atompub.events.GetFeedEvent")]

	/**
	 *  Dispatched when an Entry has been retrieved.
	 * 
	 *  @eventType com.adobe.kiwi.atompub.events.GetEntryEvent
	 **/
	[Event(name="getEntry", type="com.adobe.kiwi.atompub.events.GetEntryEvent")]

	/**
	 *  Dispatched when the edit URI for an Entry has been retrieved.
	 * 
	 *  @eventType com.adobe.kiwi.atompub.events.GetEditURIForEntryEvent
	 **/
	[Event(name="getEditURIForEntry", type="com.adobe.kiwi.atompub.events.GetEditURIForEntryEvent")]

	/**
	 *  Dispatched when an Entry has been published.
	 * 
	 *  @eventType com.adobe.kiwi.atompub.events.PublishEntryEvent
	 **/
	[Event(name="publishEntry", type="com.adobe.kiwi.atompub.events.PublishEntryEvent")]

	/**
	 *  Dispatched when an Entry has been updated.
	 * 
	 *  @eventType com.adobe.kiwi.atompub.events.UpdateEntryEvent
	 **/
	[Event(name="updateEntry", type="com.adobe.kiwi.atompub.events.UpdateEntryEvent")]
	
	/**
	 *  Dispatched when an Entry has been deleted.
	 * 
	 *  @eventType com.adobe.kiwi.atompub.events.DeleteEntryEvent
	 **/
	[Event(name="deleteEntry", type="com.adobe.kiwi.atompub.events.DeleteEntryEvent")]

	/**
	 *  Dispatched when an operation generates a Fault.
	 * 
	 *  @eventType com.adobe.kiwi.atompub.events.AtomFaultEvent
	 **/	
	[Event(name="atomFault", type="com.adobe.kiwi.atompub.events.AtomFaultEvent")]
		
	public class AnyProtocol extends EventDispatcher implements IAtomPublishingProtocol 
	{
		/** Constructor.
		 * 
		 *  @param serviceURI the URI of the Service (introspection) document
		 *  @param feedFactory factory class that generates Atom Feeds (should support
		 *  v03 for Blogger)
		 *  @param feedElementFactory factory class that generates Atom Feed Elements
		 *  (should support v03 for Blogger)
		 *  @param factory class that generates HTTPService objects for making
		 *  HTTP calls
		 **/
		public function AnyProtocol(serviceURI:URI,
			feedFactory:IAtomFeedFactory,
			feedElementFactory:IAtomFeedElementFactory, 
			serviceFactory:IServiceFactory):void
		{
			this.serviceURI = serviceURI;
			this.serviceFactory = serviceFactory;
			this.feedFactory = feedFactory;
			this.feedElementFactory = feedElementFactory;
			this.modelFactory = new ModelFactory();
		}
		
		/**
		 *  @inheritDoc
		 **/
		public function set serviceFactory(serviceFactory:IServiceFactory):void
		{
			this._serviceFactory = serviceFactory;
		}
		
		/**
		 *  @inheritDoc
		 **/
		public function get serviceFactory():IServiceFactory
		{
			return this._serviceFactory;
		}
		
		/**
		 *  @inheritDoc
		 **/
		public function set serviceURI(serviceURI:URI):void
		{
			this._serviceURI = serviceURI;
		}
		
		/**
		 *  @inheritDoc
		 **/
		public function get serviceURI():URI
		{
			return this._serviceURI;
		}
				
		/**
		 *  @inheritDoc
		 **/
		public function getService():void
		{
			var service:HTTPService = createService();
			service.url = serviceURI.toString();
			service.method = "GET";
			service.resultFormat="e4x";
			service.addEventListener("result",onGetServiceResult);
			service.addEventListener("fault",onFault);
			service.send();
		}
		
		private function onGetServiceResult(event:ResultEvent):void
		{
			// Blogger's service XML is non-standard.
			var xml:XML = new XML(event.result);
			//xml = transformBloggerServiceXML(xml);
			var atomEvent:GetServiceEvent = new GetServiceEvent();
			
			
			trace(xml.namespace() )
			
			trace(AtomConstants.APPNS)
			

			var s:IService = modelFactory.createServiceFromXML(xml);
			atomEvent.service = s;
			trace("got the service");
			trace(atomEvent.service.baseURI);
			
		trace(s.workspaces[0]);
		
		
		
			
			var workspace:IWorkspace = s.workspaces[0];
			var col:Collection = workspace.collections[0];
			trace(col.title);
			trace(col.href);
			trace(col.memberType);
			trace(col.xml);


		var collections:Array = workspace.collections;
		trace(collections);
		
		for(var key:String in collections){
			var item:Collection = collections[key];
			trace("--> " + item.title);
		}
		dispatchEvent(atomEvent);
			
		}

		/**
		 *  @inheritDoc
		 **/
		public function getFeed(collection:ICollection):void
		{
			var service:HTTPService = createService();
			service.url = collection.href.toString();
			service.method = "GET";
			service.resultFormat="e4x";
			service.addEventListener("result",onGetFeedResult);
			service.addEventListener("fault",onFault);
			service.send();
		}

		private function onGetFeedResult(event:ResultEvent):void
		{
			// Blogger's feed XML is non-standard.
			var xml:XML = new XML(event.result);
			xml = transformBloggerFeedXML(xml);
			var atomEvent:GetFeedEvent = new GetFeedEvent();
			atomEvent.feed = IAtomFeed(feedFactory.createFeedFromXML(xml));
			dispatchEvent(atomEvent);
		}
	
		/**
		 *  @inheritDoc
		 **/
		public function getEntry(uri:URI):void
		{
			var service:HTTPService = createService();
			service.url = uri.toString();
			service.method = "GET";
			service.resultFormat="e4x";
			service.addEventListener("result",onGetEntryResult);
			service.addEventListener("fault",onFault);
			service.send();
		}
		
		private function onGetEntryResult(event:ResultEvent):void
		{
			// Blogger's entry XML is non-standard.
			var xml:XML = new XML(event.result);
			xml = transformBloggerEntryXML(xml);
			var atomEvent:GetEntryEvent = new GetEntryEvent();
			atomEvent.entry = IAtomEntry(feedFactory.createEntryFromXML(xml));
			dispatchEvent(atomEvent);
		}
		
		/**
		 *  @inheritDoc
		 **/
		public function getEditURIForEntry(permalink:URI):void
		{
			// Clone this protocol object -- we don't want it to respond to
			// the events that the PermalinkFinder generates.
			var permalinkFinder:PermalinkFinder = new PermalinkFinder(this,this.makeCopy());
			permalinkFinder.getEditURIForEntry(permalink);
		}

		/**
		 *  @inheritDoc
		 **/
		public function publishEntry(entry:IAtomEntry,collection:ICollection):void
		{
			// Blogger requires a non-standard Atom entry.
			var modifiedXML:XML = transformEntryXML(entry);

			var service:HTTPService = createService();
			service.url = collection.href.toString();
			service.request = modifiedXML;
			service.contentType = "application/xml";
			service.method = "POST";
			service.resultFormat="text";
			service.addEventListener("result",onPublishEntryResult);
			service.addEventListener("fault",onFault);
			service.send();
		}

		private function onPublishEntryResult(event:ResultEvent):void
		{
			// Locate the edit URI of the published entry.
			var xml:XML = new XML(event.result);
			var atomEvent:PublishEntryEvent = new PublishEntryEvent();
			var entry:IAtomEntry = IAtomEntry(feedFactory.createEntryFromXML(xml));
			var editLink:ILink = LinkHelper.getLinkOfType(entry.links,EDIT_RELATION_TYPE);
			atomEvent.entryURI = editLink.url;
			dispatchEvent(atomEvent);
		}
		
		/**
		 *  @inheritDoc
		 **/
		public function updateEntry(entry:IAtomEntry):void
		{
			// Blogger requires a non-standard Atom entry.
			var modifiedXML:XML = transformEntryXML(entry);

			var service:HTTPService = createService();
			service.url = LinkHelper.getLinkOfType(entry.links,LinkRelationType.EDIT).url.toString();
			service.request = modifiedXML;
			service.method = "PUT";
			service.resultFormat="text";
			service.contentType = "application/xml";
			service.addEventListener("result",onUpdateEntryResult);
			service.addEventListener("fault",onFault);
			service.send();
		}

		private function onUpdateEntryResult(event:ResultEvent):void
		{
			var atomEvent:UpdateEntryEvent = new UpdateEntryEvent();
			dispatchEvent(atomEvent);
		}
		
		/**
		 *  @inheritDoc
		 **/
		public function deleteEntry(entry:IAtomEntry):void
		{
			var service:HTTPService = createService();
			service.url = LinkHelper.getLinkOfType(entry.links,LinkRelationType.EDIT).url.toString();
			service.method = "DELETE";
			service.resultFormat="text";
			service.addEventListener("result",onDeleteEntryResult);
			service.addEventListener("fault",onFault);
			service.send();
		}
		
		public function onDeleteEntryResult(event:ResultEvent):void
		{
			var atomEvent:DeleteEntryEvent = new DeleteEntryEvent();
			dispatchEvent(atomEvent);
		}
		
		private function createService():HTTPService
		{
			return serviceFactory.createHTTPService();
		}
		
		private function transformEntryXML(entry:IAtomEntry):XML
		{
			// Blogger has some funky rules:
			// - "id" can't be in the entry
			// - "updated" can't be in the entry
			// - "published" can't be in the entry.
			// - "created" can't be in the entry
			// - There must be a "generator" element on the entry
			// Ideally we would have a Blogger-specific validator that would
			// throw client exceptions if this stuff was wrong, but until I
			// put in a validation scheme, this is the simplest thing that works.
			//
			// We make a copy of the XML so that our changes don't affect the
			// caller.
			var originalXML:XML = entry.xml.copy();
			var NS:Namespace = AtomConstants.NS03;
			entry.id = null;
			entry.updated = null;
			entry.published = null;
			delete entry.xml.NS::created;
			if (entry.xml.NS::generator.length() == 0)
			{
				entry.xml.appendChild(<generator url="http://www.adobe.com">Blogger Plugin</generator>);
			}
			
			var modifiedXML:XML = entry.xml;
			entry.xml = originalXML;
			
			return modifiedXML;
		}
		
		private function transformBloggerServiceXML(xml:XML):XML
		{
			const NS:Namespace = AtomConstants.NS03;
			
			// Blogger returns its <service> document as a <feed>
			// with links for each collection.  We map the Blogger
			// format into the standard Atom format here.
			var service:IService = modelFactory.createService();
			var workspaces:Array = new Array();
			for (var i:int = 0; i < xml.NS::link.length(); i++)
			{
				var linkXML:XML = xml.NS::link[i];
				if (linkXML.@rel == FEED_RELATION_TYPE)
				{
					var workspace:IWorkspace = modelFactory.createWorkspace();
					workspace.title = linkXML.@title;
					var collection:ICollection = modelFactory.createCollection();
					collection.title = linkXML.@title;
					collection.href = new URI(linkXML.@href);
					collection.memberType = modelFactory.createMemberType();
					collection.memberType.type = "entry";
					var collections:Array = new Array();
					collections.push(collection);
					workspace.collections = collections;
					workspaces.push(workspace);
				}
			}
			service.workspaces = workspaces;
			
			return service.xml;
		}
		
		private function transformBloggerFeedXML(xml:XML):XML
		{
			// Blogger uses link relation types that don't conform to
			// the latest APP spec -- we convert them here.
			var feed:IAtomFeed = IAtomFeed(feedFactory.createFeedFromXML(xml));
			
			transformLinks(feed.links);
			for each (var entry:IAtomEntry in feed.entries)
			{
				transformLinks(entry.links);
			}
			
			return feed.xml;
		}
		
		private function transformBloggerEntryXML(xml:XML):XML
		{
			// Blogger uses link relation types that don't conform to
			// the latest APP spec -- we convert them here.
			var entry:IAtomEntry = IAtomEntry(feedFactory.createEntryFromXML(xml));
			
			transformLinks(entry.links);
			
			return entry.xml;
		}
		
		private function transformLinks(links:Array):void
		{
			for each (var link:ILink in links)
			{
				if (link.relationType == EDIT_RELATION_TYPE ||
					link.relationType == FEED_RELATION_TYPE)
				{
					link.relationType = LinkRelationType.EDIT;
				}
			}			
		}
		
		private function makeCopy():AnyProtocol
		{
			return new AnyProtocol(serviceURI,feedFactory,feedElementFactory, serviceFactory);
		}
		
		private function onFault(event:FaultEvent):void
		{
			var atomEvent:AtomFaultEvent = new AtomFaultEvent();
			atomEvent.fault = event.fault;
			dispatchEvent(atomEvent);
		}

		private static const EDIT_RELATION_TYPE:String = "service.edit";
		private static const FEED_RELATION_TYPE:String = "service.feed";
		
		private var _serviceURI:URI;
		
		private var _serviceFactory:IServiceFactory;
		private var feedFactory:IAtomFeedFactory;
		private var feedElementFactory:IAtomFeedElementFactory;
		private var modelFactory:IModelFactory;
	}
}
